#ifndef _CPROPERTYLIST_H_
#define _CPROPERTYLIST_H_

#include <buola/widgets/cwidget.h>

#include <buola/utility/usignal.h>
#include <buola/utility/uevent.h>
#include <buola/utility/uany.h>

#include <unordered_map>

namespace buola { namespace gui {

class CPLField
{
private:
    CPLField(CPropertyField*);      //TYPE_FIELD
    CPLField(const std::wstring&);      //TYPE_STARTGROUP
    CPLField();                     //TYPE_ENDGROUP
    CPLField(const img::CPicture&,const std::wstring&,const std::wstring&);   //TYPE_TITLE

public:
    int mType;

    CPoint_d mPos;
    CSize_d mSize;
    CPropertyField *mField;
    UAny mValue;
    bool mExpanded;
    bool mVisible;

    std::wstring mTitle;
    std::wstring mSubTitle;
    img::CPicture mImage;

    UAny mClosure;

    friend class CPropertyList;
};

class CPropertyList : public CWidget
{
public:
    enum
    {
        STYLE_NOICON=0x00000100,
        STYLE_USERMENU=0x00000200
    };

    CPropertyList();
    ~CPropertyList();

//Managing fields
    void AddTitle(const img::CPicture &pImage,const std::wstring &pTitle,
                  const std::wstring &pSubTitle=std::wstring());
    CPLField *AddField(CPropertyField*,const UAny &pClosure=UAny());
    CPLField *AddFieldAndValue(CPropertyField*,const UAny&,
                            const UAny &pClosure=UAny());
    void RemoveField(int);
    void RemoveAllFields();
    void AddFields(std::vector<CPropertyField*>&,const UAny &pClosure=UAny());
    void AddFields(std::vector<std::unique_ptr<CPropertyField>>&,const UAny &pClosure=UAny());
    void AddFieldsAndValues(const std::map<CPropertyField*,UAny>&,const UAny &pClosure=UAny());
    void SetValue(const std::wstring &pName,const UAny &pValue);
    void SetValue(CPropertyField*,const UAny &pValue);
    void StartGroup(const std::wstring &pName,bool pExpanded=true);
    void EndGroup();
    void ExpandGroup(const std::wstring &pName);
    void CollapseGroup(const std::wstring &pName);

    void Center(CPLField*);

    void SetIconStyle(int);

public:
////////////// Event handling //////////////
    virtual void OnPreExpose();
    virtual void OnExpose(const CExposeEvent &pE);
    void OnVScroll(int pPos);
    virtual void OnButtonDown(const CMouseEvent &pE);
    virtual void OnButtonUp(const CMouseEvent &pE);
    virtual void OnMouseMove(const CMouseEvent &pE);
    virtual void OnMouseWheel(const CMouseEvent &pE);
    virtual void OnSize(const CGeometryEvent &pE);

    virtual void OnStyleChanged(TStyle);

    virtual void OnFillMenu(PMenu,bool) {}

protected:
/////////////// protected functions //////////////
    void DrawAll(img::CGC*);
    void DrawAll(img::CGC*,const CRect_d &pRect);

    void DrawField(img::CGC*,CPLField*);

    void ShapeAll();
    void ShapeField(CPLField*,img::PFont);

    void PosAll();

    void Update();

    int HitTestField(CPLField*,const CPoint_d&);

    CPLField *GetFieldAt(const CPoint_d&,int *pWhere=nullptr);

    void StartEditing(CPLField*);
    void EndEditing();

public:
    USignal<void(PMenu,CPropertyField*,bool,UAny)> sFillMenu;
    UEvent<void(CPropertyField*,UAny,UAny)> eChanged;
    USignal<void(CPropertyField*)> sSelected;
    USignal<void(const std::wstring&,bool)> sExpanded;

protected:
    CSize_d mIconSize;
    CSize_d mTextSize;

    PScrollBar mVScroll;

    CPLField *mSelected;
    CPLField *mEditing;

    CPropertyEditor *mEditor;

    bool mIsPosValid;
    bool mIsDrawing;

    int mIconStyle;

    std::vector<CPLField*> mFields;
    std::unordered_map<std::wstring,CPLField*> mHashTable;
    
    CPoint_d mScrollPos;
};

/*namespace gui*/ } /*namespace buola*/ }

#endif
